﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WebPALTT.Lib
{
    public class Aggregate
    {
        List<double> values = new List<double> { };
        protected double mMean = 0.0;
        protected double mMin = 0.0;
        protected double mMax = 0.0;
        protected double mStdDev = 0.0;
        protected double mMedian = 0.0;
        protected double mUpperQuartile = 0.0;
        protected double mLowerQuartile = 0.0;
        protected bool dirty = false;

        public void AddValue(double value)
        {
            for (int i = 0; i < values.Count; i++)
                if (value < values[i])
                {
                    values.Insert(i, value);
                    dirty = true;
                    return;
                }
            values.Add(value);
            dirty = true;
        }

        protected void Calculate()
        {
            if (dirty && values.Count > 0)
            {
                double total = 0.0;
                double sumofdev = 0.0;
                for (int i = 0; i < values.Count; i++)
                    total += values[i];
                double mean = (double)(total / values.Count);

                for (int i = 0; i < values.Count; i++)
                    sumofdev += Math.Pow(values[i] - mean, 2.0);
                double stddev = values.Count == 1 ? 0 : Math.Sqrt(sumofdev / (values.Count - 1));
                mMean = mean;
                mStdDev = stddev;

                List<double> array = new List<double> { };
                array.AddRange(values.ToArray());

                mMedian = GetMedian(array.ToArray());
                if (array.Count > 1 && array.Count % 2 == 1)
                    array.RemoveAt(values.Count / 2);
                mLowerQuartile = GetMedian(array.GetRange(0, array.Count / 2).ToArray());
                mUpperQuartile = GetMedian(array.GetRange(array.Count / 2, array.Count / 2).ToArray());
                dirty = false;
            }
        }

        private static double GetMedian(double[] array)
        {
            if (array == null || array.Length == 0)
                return -1;
            if (array.Length % 2 == 0)
                return (array[(array.Length - 1) / 2] + array[((array.Length - 1) / 2) + 1]) / 2;
            else
                return array[array.Length / 2];
        }

        public int Count { get { return values.Count; } }

        public double Mean { get { Calculate(); return mMean; } }
        public double Min { get { return values.Count > 0 ? values.First() : 0.0; } }
        public double Max { get { return values.Count > 0 ? values.Last() : 0.0; } }
        public double Median { get { Calculate(); return mMedian; } }
        public double UpperQuartile { get { Calculate(); return mUpperQuartile; } }
        public double LowerQuartile { get { Calculate(); return mLowerQuartile; } }
        public double StdDev { get { Calculate(); return mStdDev; } }
    }
}
